package com.yu1996.gulimall.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.yu1996.gulimall.product.service.CategoryBrandRelationService;
import com.yu1996.gulimall.product.vo.Catelog2Vo;
import com.yu1996.gulimall.product.vo.Catelog2Vo.Catelog3Vo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yu1996.common.utils.PageUtils;
import com.yu1996.common.utils.Query;

import com.yu1996.gulimall.product.dao.CategoryDao;
import com.yu1996.gulimall.product.entity.CategoryEntity;
import com.yu1996.gulimall.product.service.CategoryService;
import org.springframework.util.StringUtils;

@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity>
    implements CategoryService {

  @Autowired private CategoryBrandRelationService categoryBrandRelationService;
  @Autowired private StringRedisTemplate stringRedisTemplate;

  @Override
  public PageUtils queryPage(Map<String, Object> params) {
    IPage<CategoryEntity> page =
        this.page(new Query<CategoryEntity>().getPage(params), new QueryWrapper<CategoryEntity>());

    return new PageUtils(page);
  }

  /** Description: 根据树形结构查询所有的分析类 Date: 2021/8/19 Author: yu Return: 树形结构的分类集合 */
  public List<CategoryEntity> listForTree() {
    // 1、查询所有的分类集合
    List<CategoryEntity> allCategory = baseMapper.selectList(null);
    // 2、组装父子的树级结构
    // 2.1、获得一级分类
    List<CategoryEntity> collectLv1 =
        allCategory.stream()
            .filter(
                (entity -> {
                  return entity.getParentCid() == 0;
                }))
            .map(
                (entity -> {
                  entity.setChildren(getChildren(entity, allCategory));
                  return entity;
                }))
            .sorted(
                (menu1, menu2) -> {
                  return (menu1.getSort() == null ? 0 : menu1.getSort())
                      - (menu2.getSort() == null ? 0 : menu2.getSort());
                })
            .collect(Collectors.toList());

    return collectLv1;
  }

  /** Description: 根据id查询出三级分类的id路径 Param: 查询的三级分类id Date: 2021/8/26 Author: yu Return: id路径的数组 */
  @Override
  public Long[] findCatelogPath(Long catelogId) {

    List<Long> parentPath = findParentPath(catelogId, new ArrayList<>());
    // 将集合倒叙输出
    Collections.reverse(parentPath);
    return parentPath.toArray(new Long[parentPath.size()]);
  }

  /** 级联修改 */
  public void updateCascade(CategoryEntity category) {
    // 首先更新自己
    this.updateById(category);
    if (!StringUtils.isEmpty(category.getName())) {
      categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
    }
    // TODO 更新其他关联的冗余字段
  }

  /**
   * 查询分类一级标题返回
   *
   * @return
   */
  @Cacheable({"category"}) // 如果缓存中有则不调用方法返回缓存数据   如果缓存中没有再调用，默认过期时间-1，缓存名字：category::SimpleKey []
  @Override
  public List<CategoryEntity> getLever1Categorys() {
    List<CategoryEntity> categoryEntities =
        this.baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
    return categoryEntities;
  }

  // TODO  压力测试会产生堆外内存溢出问题  OutOfDirectMemoryError
  // 1、springboot2.0后redis默认使用lettuce作为redis的客户端。而lettuce是使用netty进行网络通信
  // 2、lettuce的bug导致netty的堆外内存溢出  netty如果没有指定堆外内存大小 默认使用-Xmx配置的内存大小
  // 3、可以通过-Dio.netty.maxDirectMemory进行设置
  // 解决方案：：：
  // 1、升级lettuce客户端
  // 2、切换使用jedis
  @Override
  public Map<String, List<Catelog2Vo>> getCatelogJson() {
    /** 使用这种缓存锁存在如下几个问题 推荐使用分布式锁 1、空结果缓存 解决缓存穿透 2、设置（随机）过期时间 解决缓存雪崩 3、加锁 解决缓存击穿 */
    //    synchronized (this){
    //
    //    }
    // 1、从缓存中查找有没有分类数据
    Map<String, List<Catelog2Vo>> catelogs = null;
    ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
    String catelogJSON = ops.get("catelogJSON");
    if (StringUtils.isEmpty(catelogJSON)) {
      // 2、缓存中没有数据  需要查找数据库并并保存到缓存中
      catelogs = getCatelogJsonFromDB();
      String jsonCatelogs = JSON.toJSONString(catelogs);
      ops.set("catelogJSON", jsonCatelogs);
    }
    // 3、如果缓存中有，则将缓存中的数据转换成对应格式返回
    catelogs = JSON.parseObject(catelogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {});
    return catelogs;
  }

  /**
   * 使用Redisson分布式锁
   *
   * @return
   */
  public Map<String, List<Catelog2Vo>> getCatelogJsonFromDBWithRedisson() {

    // 1、从缓存中查找有没有分类数据
    Map<String, List<Catelog2Vo>> catelogs = null;
    ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
    String catelogJSON = ops.get("catelogJSON");
    if (StringUtils.isEmpty(catelogJSON)) {
      // 2、缓存中没有数据  需要查找数据库并并保存到缓存中
      catelogs = getCatelogJsonFromDB();
      String jsonCatelogs = JSON.toJSONString(catelogs);
      ops.set("catelogJSON", jsonCatelogs);
    }
    // 3、如果缓存中有，则将缓存中的数据转换成对应格式返回
    catelogs = JSON.parseObject(catelogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {});
    return catelogs;
  }

  /**
   * 从数据库中查询出三级分类数据并封装到Vo中返回
   *
   * @return
   */
  public Map<String, List<Catelog2Vo>> getCatelogJsonFromDB() {

    /** 优化查询三级分类步骤 将嵌套查询数据库变成 只查询一次数据库 */
    // 先查询出所有的一级分类
    List<CategoryEntity> allCategory = this.baseMapper.selectList(null);

    List<CategoryEntity> lever1Categorys = getParent_cid(allCategory, 0L);
    Map<String, List<Catelog2Vo>> categroyVo = null;
    if (lever1Categorys != null) {
      // 根据一级分类的id查出二级分类，并封装到Catelog2Vo中
      categroyVo =
          lever1Categorys.stream()
              .collect(
                  Collectors.toMap(
                      k -> k.getCatId().toString(),
                      v -> {
                        List<CategoryEntity> category2Entities =
                            getParent_cid(allCategory, v.getCatId());
                        List<Catelog2Vo> catelog2Vos = null;
                        // 封装二级分类VO
                        if (category2Entities != null) {
                          catelog2Vos =
                              category2Entities.stream()
                                  .map(
                                      cate2 -> {
                                        Catelog2Vo catelog2Vo =
                                            new Catelog2Vo(
                                                cate2.getParentCid().toString(),
                                                null,
                                                cate2.getCatId().toString(),
                                                cate2.getName());
                                        // 根据二级分类的id查出三级分类，并封装到Catelog3Vo中
                                        List<CategoryEntity> catelog3Entitys =
                                            getParent_cid(allCategory, cate2.getCatId());
                                        // 封装三级分类VO数据
                                        List<Catelog3Vo> catelog3VoList = null;
                                        if (catelog3Entitys != null) {
                                          catelog3VoList =
                                              catelog3Entitys.stream()
                                                  .map(
                                                      cate3 -> {
                                                        return new Catelog3Vo(
                                                            cate3.getParentCid().toString(),
                                                            cate3.getCatId().toString(),
                                                            cate3.getName());
                                                      })
                                                  .collect(Collectors.toList());
                                        }
                                        catelog2Vo.setCatalog3List(catelog3VoList);
                                        return catelog2Vo;
                                      })
                                  .collect(Collectors.toList());
                          return catelog2Vos;
                        }
                        return catelog2Vos;
                      }));
    }
    //    // 先查询出所有的一级分类
    //    List<CategoryEntity> lever1Categorys = getLever1Categorys();
    //    Map<String, List<Catelog2Vo>> categroyVo = null;
    //    if (lever1Categorys != null) {
    //      // 根据一级分类的id查出二级分类，并封装到Catelog2Vo中
    //      categroyVo =
    //          lever1Categorys.stream()
    //              .collect(
    //                  Collectors.toMap(
    //                      k -> k.getCatId().toString(),
    //                      v -> {
    //                        List<CategoryEntity> category2Entities =
    //                            this.list(
    //                                new QueryWrapper<CategoryEntity>().eq("parent_cid",
    // v.getCatId()));
    //                        List<Catelog2Vo> catelog2Vos = null;
    //                        //封装二级分类VO
    //                        if (category2Entities != null) {
    //                          catelog2Vos =
    //                              category2Entities.stream()
    //                                  .map(
    //                                      cate2 -> {
    //                                        Catelog2Vo catelog2Vo =
    //                                            new Catelog2Vo(
    //                                                cate2.getParentCid().toString(),
    //                                                null,
    //                                                cate2.getCatId().toString(),
    //                                                cate2.getName());
    //                                        // 根据二级分类的id查出三级分类，并封装到Catelog3Vo中
    //                                        List<CategoryEntity> catelog3Entitys =
    //                                            this.list(
    //                                                new QueryWrapper<CategoryEntity>()
    //                                                    .eq("parent_cid", cate2.getCatId()));
    //                                        // 封装三级分类VO数据
    //                                        List<Catelog3Vo> catelog3VoList = null;
    //                                        if (catelog3Entitys != null) {
    //                                          catelog3VoList =
    //                                              catelog3Entitys.stream()
    //                                                  .map(
    //                                                      cate3 -> {
    //                                                        return new Catelog3Vo(
    //                                                            cate3.getParentCid().toString(),
    //                                                            cate3.getCatId().toString(),
    //                                                            cate3.getName());
    //                                                      })
    //                                                  .collect(Collectors.toList());
    //                                        }
    //                                        catelog2Vo.setCatalog3List(catelog3VoList);
    //                                        return catelog2Vo;
    //                                      })
    //                                  .collect(Collectors.toList());
    //                          return catelog2Vos;
    //                        }
    //                        return catelog2Vos;
    //                      }));
    //    }
    return categroyVo;
  }

  /**
   * 收取父id为指定值的集合
   *
   * @param categorys 抽取的集合
   * @param parent_cid 父id
   * @return
   */
  private List<CategoryEntity> getParent_cid(List<CategoryEntity> categorys, Long parent_cid) {
    return categorys.stream()
        .filter(cate -> Objects.equals(cate.getParentCid(), parent_cid))
        .collect(Collectors.toList());
  }

  /**
   * @param catelogId
   * @param ids
   * @return
   */
  private List<Long> findParentPath(Long catelogId, List<Long> ids) {
    // 先将查询的id放入数组中
    ids.add(catelogId);
    // 判断是否还有父节点，如果有继续递归
    CategoryEntity category = this.getById(catelogId);
    if (category.getParentCid() != 0) {
      findParentPath(category.getParentCid(), ids);
    }
    return ids;
  }

  /** Description: 获得所有的子孩子 Param: 当前需要查找子孩子的分类，所有分类 Date: 2021/8/19 Author: yu Return: 所有的子孩子 */
  private List<CategoryEntity> getChildren(CategoryEntity root, List<CategoryEntity> all) {
    List<CategoryEntity> collect =
        all.stream()
            .filter(
                (entity) -> {
                  return root.getCatId().equals(entity.getParentCid());
                })
            .map(
                entity -> {
                  entity.setChildren(getChildren(entity, all));
                  return entity;
                })
            .sorted(
                (menu1, menu2) -> {
                  return (menu1.getSort() == null ? 0 : menu1.getSort())
                      - (menu2.getSort() == null ? 0 : menu2.getSort());
                })
            .collect(Collectors.toList());
    return collect;
  }
}
